package com.torry.edss.domain.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.torry.edss.application.cache.TradingDayCache;
import com.torry.edss.domain.accessor.CompanyAccessor;
import com.torry.edss.domain.accessor.HisDailyLineAccessor;
import com.torry.edss.domain.accessor.LimitUpAccessor;
import com.torry.edss.domain.accessor.TacticCrossStarSnipeAccessor;
import com.torry.edss.domain.enums.TacticMode;
import com.torry.edss.domain.annotations.TacticBean;
import com.torry.edss.domain.model.Company;
import com.torry.edss.domain.model.HisDailyLine;
import com.torry.edss.domain.model.LimitUp;
import com.torry.edss.domain.model.TacticCrossStarSnipe;
import com.torry.edss.domain.service.TacticService;
import com.torry.edss.domain.valueobject.LimitType;
import com.torry.edss.domain.valueobject.MarketPlate;
import com.torry.edss.infras.entity.LimitUpDO;
import com.torry.edss.infras.entity.TacticCrossStarSnipeDO;
import com.torry.edss.infras.util.DateUtil;
import com.torry.edss.infras.util.NumberUtils;
import com.torry.edss.integration.facade.RemoteApiFacade;
import com.torry.edss.integration.facade.enums.Direction;
import com.torry.edss.integration.facade.result.FetchFundFlowInfoResult;
import com.torry.edss.integration.facade.result.FetchDailyLineResult;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author torry.wang
 */
@TacticBean(mode = TacticMode.CROSS_STAR)
@Slf4j
public class CrossStarTacticServiceImpl implements TacticService {

    @Resource
    private RemoteApiFacade remoteApiFacade;
    @Resource
    private LimitUpAccessor limitUpAccessor;
    @Resource
    private CompanyAccessor companyAccessor;
    @Resource
    private HisDailyLineAccessor hisDailyLineAccessor;
    @Resource
    private TacticCrossStarSnipeAccessor tacticCrossStarSnipeAccessor;


    @Override
    public boolean realTimePicking() {
        try {
            String date = DateUtil.formatDate(LocalDate.now());
//            String date = "2025-05-09";
            FetchFundFlowInfoResult result = remoteApiFacade.fetchFundFlowInfo(date);
            if (result == null) return false;

            Set<Long> companyIds = new HashSet<>();
            Map<Long, BigDecimal> guardMap = new HashMap<>();
            Map<Long, String> intervalMap = new HashMap<>();
            /*
             * 涨停板  2板（含）以内
             * 调整2-4天
             * 涨停放量 < 2倍
             */
            int index = TradingDayCache.getAllTradingDays().size();
            List<String> dates = TradingDayCache.getAllTradingDays().subList(index - 5, index - 1);
            List<LimitUp> limitUps = limitUpAccessor.list(new LambdaQueryWrapper<LimitUpDO>()
                    .in(LimitUpDO::getDate, dates).le(LimitUpDO::getContinuousCount, 2).apply("quantity / previous_day_quantity < 2"));

            for (LimitUp limitUp : limitUps) {
                Long companyId = limitUp.getCompanyId();
                Company company = companyAccessor.getById(companyId);
                // 必须是主板
                if (company.getPlate() != MarketPlate.HSZB || company.getName().contains("ST") || company.getName().contains("退") || company.getDelisted()) {
                    continue;
                }
                if (limitUp.getMarketValue() >= 20000000000D) {
                    log.info("{}, {}, 流值 > 200亿", date, company.getName());
                    continue;
                }
                // 选股前没有再次涨停
                List<LimitUp> list = limitUpAccessor.list(new LambdaQueryWrapper<LimitUpDO>()
                        .eq(LimitUpDO::getCompanyId, limitUp.getCompanyId())
                        .gt(LimitUpDO::getDate, limitUp.getDate())
                        .le(LimitUpDO::getDate, date));
                if (CollectionUtils.isNotEmpty(list)) continue;

                // 首阴放量必须小于2.5倍
                String limitDate = DateUtil.formatDate(limitUp.getDate());
                FetchDailyLineResult firstAdjust = remoteApiFacade.fetchNexDayLine(company.getCode(), limitDate);
                if (firstAdjust == null) continue;
                if (firstAdjust.getVolume() / limitUp.getQuantity() >= 2.5) {
                    log.info("{}, {}，首阴量能 > 2.5倍", date, company.getName());
                    continue;
                }

                FetchDailyLineResult limitKLineData = remoteApiFacade.fetchDayLine(company.getCode(), limitDate, Direction.CURRENT);
                FetchDailyLineResult previousKLine = remoteApiFacade.fetchDayLine(company.getCode(), TradingDayCache.getLastTradingDay(), Direction.CURRENT);

                if (previousKLine == null) continue;
                // 前一日必须收阴
                if (previousKLine.getUpPercentage() > 0) {
                    log.info("{}, {}, 前一日收阳", date, company.getName());
                    continue;
                }

                // 买入当日 必须缩量
                // 买入当日 必须跳空低开
                // 买入当日 涨幅必须小于0
                // 买入当日 收十字星。实体部分不能超过0.5%
                FetchDailyLineResult kLineData = remoteApiFacade.fetchDayLine(company.getCode(), date, Direction.CURRENT);
                if (limitUp.getContinuousCount() == 1 && kLineData.getLow() < limitKLineData.getLow()) {
                    log.info("{}, {}，首板，跌破涨停板最低位", date, company.getName());
                    continue;
                }
                if (limitUp.getContinuousCount() == 2) {
                    FetchDailyLineResult firstLimitData = remoteApiFacade.fetchPreviousDayLine(company.getCode(), limitKLineData.getDate());
                    if (kLineData.getLow() < Math.min(firstLimitData.getPrice(), limitKLineData.getLow())) {
                        log.info("{}, {}，2板，跌破保护位（首板最高位 或 二板最低位）", date, company.getName());
                        continue;
                    }
                }
                if (kLineData.getVolume() >= previousKLine.getVolume()) {
                    log.info("{}, {}，当日放量", date, company.getName());
                    continue;
                }
                if (kLineData.getOpen() >= Math.min(previousKLine.getPrice(), previousKLine.getOpen())) {
                    log.info("{}, {}，当日非低开", date, company.getName());
                    continue;
                }
                // 实时选股，不再限制必须收阴。涨幅 < 0.5即可。 尾盘观察。
                if (kLineData.getUpPercentage() > 0.5) {
                    log.info("{}, {}，当日涨幅 > 0.5", date, company.getName());
                    continue;
                }

                double o = kLineData.getOpen();
                double c = kLineData.getPrice();
                double h = kLineData.getHigh();
                double l = kLineData.getLow();

                // 当天一字板
                if ((o == h) && (l == c) && (o == c)) {
                    log.info("{}, {}，当日一字板", date, company.getName());
                    continue;
                }
                // 无上影线 或 无下影线
                if (o == h || o == l || c == h || c == l) {
                    log.info("{}, {}，无上/下影线", date, company.getName());
                    continue;
                }

                // K线实体高度
                double oPercent = (o - previousKLine.getPrice()) / previousKLine.getPrice() * 100;
                double cPercent = (c - previousKLine.getPrice()) / previousKLine.getPrice() * 100;

                // 实时选股，不再限制实体部分必须 <0.5 。 < 1即可。 尾盘观察。
                if (Math.abs(oPercent - cPercent) > 1) {
                    log.info("{}, {}，实体 >1%", date, company.getName());
                    continue;
                }

                // 上下影线长度相差 > 3， 不对称
                double hPercent = (h - previousKLine.getPrice()) / previousKLine.getPrice() * 100;
                double lPercent = (l - previousKLine.getPrice()) / previousKLine.getPrice() * 100;
                double up = Math.abs(hPercent - Math.max(oPercent, cPercent));
                double down = Math.abs(Math.min(oPercent, cPercent) - lPercent);
                if (Math.abs(up - down) >= 3) {
                    log.info("{}, {}，上/下影线过长", date, company.getName());
                    continue;
                }

                // 阴线必须缩量
                List<FetchDailyLineResult> allKLineData = remoteApiFacade.fetchAllDailyLines(company.getCode());
                List<String> allDates = allKLineData.stream().map(FetchDailyLineResult::getDate).collect(Collectors.toList());
                int limitIndex = allDates.indexOf(DateUtil.formatDate(limitUp.getDate()));
                //int endIndex = allDates.indexOf(date);

                boolean notYinSuo = false;
                for (int i = limitIndex + 2; i < allDates.size(); i++) {
                    if (allKLineData.get(i).getUpPercentage() < 0 && allKLineData.get(i).getVolume() >= allKLineData.get(i - 1).getVolume()) {
                        notYinSuo = true;
                        log.info("{}, {}，阴线不是缩量", date, company.getName());
                        break;
                    }
                }
                if (notYinSuo) continue;
                TacticCrossStarSnipe tacticCrossStarSnipe = new TacticCrossStarSnipe();
                tacticCrossStarSnipe.setCompanyId(companyId);
                tacticCrossStarSnipe.setLimitId(limitUp.getId());
                tacticCrossStarSnipe.setLimitType(LimitType.UP);
                tacticCrossStarSnipe.setDate(DateUtil.parseDate(date));
                tacticCrossStarSnipe.setPurchaseDayClosePrice(BigDecimal.valueOf(c));
                tacticCrossStarSnipe.setPurchaseDayOpenPrice(BigDecimal.valueOf(o));
                tacticCrossStarSnipe.setPurchaseDayMaxPrice(BigDecimal.valueOf(h));
                tacticCrossStarSnipe.setPurchaseDayMinPrice(BigDecimal.valueOf(l));
                tacticCrossStarSnipe.setPurchaseDayOpenUpRatio(NumberUtils.format(oPercent));
                tacticCrossStarSnipe.setPurchaseDayCloseUpRatio(NumberUtils.format(cPercent));
                tacticCrossStarSnipe.setPurchaseDayMaxUpRatio(NumberUtils.format(hPercent));
                tacticCrossStarSnipe.setPurchaseDayMinUpRatio(NumberUtils.format(lPercent));
                tacticCrossStarSnipe.setPurchaseDayQuantity(kLineData.getVolume());
                tacticCrossStarSnipe.setPreviousDayOpenPrice(BigDecimal.valueOf(previousKLine.getOpen()));
                tacticCrossStarSnipe.setPreviousDayClosePrice(BigDecimal.valueOf(previousKLine.getPrice()));
                tacticCrossStarSnipe.setPreviousDayMaxPrice(BigDecimal.valueOf(previousKLine.getHigh()));
                tacticCrossStarSnipe.setPreviousDayMinPrice(BigDecimal.valueOf(previousKLine.getLow()));
                tacticCrossStarSnipe.setPreviousDayCloseUpRatio(previousKLine.getUpPercentage());

                // TODO
//                tacticCrossStarSnipe.setPreviousDayCloseUpRatio();
//                tacticCrossStarSnipe.setPreviousDayMaxUpRatio();
//                tacticCrossStarSnipe.setPreviousDayOpenUpRatio();
//                tacticCrossStarSnipe.setPreviousDayMinUpRatio();
                tacticCrossStarSnipe.setPreviousDayQuantity(previousKLine.getVolume());

                tacticCrossStarSnipe.setFirstAdjustQuantity(firstAdjust.getVolume());
                tacticCrossStarSnipe.setAdjustDays(allDates.size() - limitIndex);

                if (limitUp.getContinuousCount() == 1) {
                    if (c >= limitKLineData.getPrice()) {
                        tacticCrossStarSnipe.setGuardPrice(BigDecimal.valueOf(limitKLineData.getPrice()));
                    } else {
                        tacticCrossStarSnipe.setGuardPrice(BigDecimal.valueOf(limitKLineData.getLow()));
                    }
                }
                if (limitUp.getContinuousCount() == 2) {
                    if (c > limitKLineData.getPrice()) {
                        tacticCrossStarSnipe.setGuardPrice(BigDecimal.valueOf(limitKLineData.getPrice()));
                    } else {
                        FetchDailyLineResult firstLimitData = remoteApiFacade.fetchPreviousDayLine(company.getCode(), limitKLineData.getDate());
                        double min = Math.min(firstLimitData.getPrice(), limitKLineData.getLow());
                        tacticCrossStarSnipe.setGuardPrice(BigDecimal.valueOf(min));
                    }
                }
                companyIds.add(tacticCrossStarSnipe.getCompanyId());
                guardMap.put(tacticCrossStarSnipe.getCompanyId(), tacticCrossStarSnipe.getGuardPrice());
                BigDecimal multiply = tacticCrossStarSnipe.getPreviousDayClosePrice().multiply(new BigDecimal("0.005"));
                BigDecimal MAX = tacticCrossStarSnipe.getPurchaseDayOpenPrice().add(multiply);
                BigDecimal MIN = tacticCrossStarSnipe.getPurchaseDayOpenPrice().subtract(multiply);
                intervalMap.put(tacticCrossStarSnipe.getCompanyId(), NumberUtils.format(MIN, 3) + "~" + NumberUtils.format(MAX, 3));
            }
            if (CollectionUtils.isNotEmpty(companyIds)) {
                //dingTalkClient.sendMsg(companyIds, guardMap, intervalMap);
                System.out.println(StringUtils.join(companyIds));
            } else {
                log.info("没有符合模式的股票");
            }


        } catch (Exception e) {
            e.printStackTrace();
        }
        return true;
    }

    @Override
    public boolean hisPicking(String date) {
        try {
            if (!TradingDayCache.isTradingDay(date)) return true;
            /*
             * 涨停板  2板（含）以内
             * 调整2-4天
             * 涨停放量 < 2倍
             */
            int index = TradingDayCache.getAllTradingDays().indexOf(date);
            List<String> dates = TradingDayCache.getAllTradingDays().subList(index - 5, index - 1);
            List<LimitUp> limitUps = limitUpAccessor.findByDateAndContinuousCount(dates, Arrays.asList(1, 2), 2D);
            for (LimitUp limitUp : limitUps) {
                Long companyId = limitUp.getCompanyId();
                Company company = companyAccessor.getById(companyId);
                // 必须是主板
                if (company.getPlate() != MarketPlate.HSZB || company.getName().contains("ST") || company.getDelisted()) {
                    continue;
                }
                if (limitUp.getMarketValue() >= 20000000000D) continue;

                // 选股前没有再次涨停
                List<LimitUp> list = limitUpAccessor.list(new LambdaQueryWrapper<LimitUpDO>()
                        .eq(LimitUpDO::getCompanyId, limitUp.getCompanyId())
                        .gt(LimitUpDO::getDate, limitUp.getDate())
                        .le(LimitUpDO::getDate, date));
                if (CollectionUtils.isNotEmpty(list)) {
                    log.info("{}, {}， 出现再次涨停", date, company.getName());
                    continue;
                }

                String code = company.getCode();
                String limitDate = DateUtil.formatDate(limitUp.getDate());
                // 首阴放量必须小于2倍
                HisDailyLine firstAdjust = hisDailyLineAccessor.getNextDayLine(code, limitDate);
                if (firstAdjust == null) continue;
                if (firstAdjust.getVolume() / limitUp.getQuantity() >= 2.5) {
                    log.info("{}, {}，首阴量能 > 2.5倍", date, company.getName());
                    continue;
                }

                HisDailyLine limitKLineData = hisDailyLineAccessor.getDayLine(code, limitDate);
                HisDailyLine previousKLine;
                try {
                    previousKLine = hisDailyLineAccessor.getPreviousDayLine(code, date);
                } catch (Exception x) {
                    continue;
                }

                // 前一日必须收阴
                if (previousKLine.getUpPercentage() > 0) {
                    log.info("{}, {}, 前一日收阳", date, company.getName());
                    continue;
                }

                // 买入当日 必须缩量
                // 买入当日 必须跳空低开
                // 买入当日 涨幅必须小于0
                // 买入当日 收十字星。实体部分不能超过0.5%
                HisDailyLine kLineData = hisDailyLineAccessor.getDayLine(code, date);
                if (limitUp.getContinuousCount() == 1 && kLineData.getLow() < limitKLineData.getLow()) {
                    log.info("{}, {}，首板，跌破涨停板最低位", date, company.getName());
                    continue;
                }
                if (limitUp.getContinuousCount() == 2) {
                    HisDailyLine firstLimitData = hisDailyLineAccessor.getPreviousDayLine(code, DateUtil.formatDate(limitKLineData.getDate()));
                    if (kLineData.getLow() < Math.min(firstLimitData.getClose(), limitKLineData.getLow())) {
                        log.info("{}, {}，2板，跌破保护位（首板最高位 或 二板最低位）", date, company.getName());
                        continue;
                    }
                }
                if (kLineData.getVolume() >= previousKLine.getVolume()) {
                    log.info("{}, {}，当日放量", date, company.getName());
                    continue;
                }
                if (kLineData.getOpen() >= Math.min(previousKLine.getClose(), previousKLine.getOpen())) {
                    log.info("{}, {}，当日非低开", date, company.getName());
                    continue;
                }
                if (kLineData.getUpPercentage() > 0) {
                    log.info("{}, {}，当日涨幅 > 0", date, company.getName());
                    continue;
                }

                double o = kLineData.getOpen();
                double c = kLineData.getClose();
                double h = kLineData.getHigh();
                double l = kLineData.getLow();

                // 当天一字板
                if ((o == h) && (l == c) && (o == c)) {
                    log.info("{}, {}，当日一字板", date, company.getName());
                    continue;
                }
                // 无上影线 或 无下影线
                if (o == h || o == l || c == h || c == l) {
                    log.info("{}, {}，无上/下影线", date, company.getName());
                    continue;
                }

                // K线实体高度
                double oPercent = (o - previousKLine.getClose()) / previousKLine.getClose() * 100;
                double cPercent = (c - previousKLine.getClose()) / previousKLine.getClose() * 100;

                if (Math.abs(oPercent - cPercent) > 0.5) {

                    log.info("{}, {}，实体 > 0.5%, {}", date, company.getName(), Math.abs(oPercent - cPercent));
                    continue;
                }

                // 上下影线长度相差
                double hPercent = (h - previousKLine.getClose()) / previousKLine.getClose() * 100;
                double lPercent = (l - previousKLine.getClose()) / previousKLine.getClose() * 100;
                double up = Math.abs(hPercent - Math.max(oPercent, cPercent));
                double down = Math.abs(Math.min(oPercent, cPercent) - lPercent);
                if (Math.abs(up - down) >= 3) {
                    log.info("{}, {}，上/下影线过长", date, company.getName());
                    continue;
                }

                // 阴线必须缩量
                List<HisDailyLine> allKLineData = hisDailyLineAccessor.findAllDayLines(code);
                List<String> allDates = allKLineData.stream().map(e -> DateUtil.formatDate(e.getDate())).collect(Collectors.toList());
                int limitIndex = allDates.indexOf(DateUtil.formatDate(limitUp.getDate()));
                int endIndex = allDates.indexOf(date);

                boolean notYinSuo = false;
                for (int i = limitIndex + 2; i < endIndex; i++) {
                    if (allKLineData.get(i).getUpPercentage() < 0 && allKLineData.get(i).getVolume() >= allKLineData.get(i - 1).getVolume()) {
                        notYinSuo = true;
                        log.info("{}, {}，阴线不是缩量", date, company.getName());
                        break;
                    }
                }
                if (notYinSuo) continue;
                TacticCrossStarSnipe tacticCrossStarSnipe = new TacticCrossStarSnipe();
                tacticCrossStarSnipe.setCompanyId(companyId);
                tacticCrossStarSnipe.setLimitId(limitUp.getId());
                tacticCrossStarSnipe.setLimitType(LimitType.UP);
                tacticCrossStarSnipe.setDate(DateUtil.parseDate(date));
                tacticCrossStarSnipe.setPurchaseDayClosePrice(BigDecimal.valueOf(c));
                tacticCrossStarSnipe.setPurchaseDayOpenPrice(BigDecimal.valueOf(o));
                tacticCrossStarSnipe.setPurchaseDayMaxPrice(BigDecimal.valueOf(h));
                tacticCrossStarSnipe.setPurchaseDayMinPrice(BigDecimal.valueOf(l));
                tacticCrossStarSnipe.setPurchaseDayOpenUpRatio(NumberUtils.format(oPercent));
                tacticCrossStarSnipe.setPurchaseDayCloseUpRatio(NumberUtils.format(cPercent));
                tacticCrossStarSnipe.setPurchaseDayMaxUpRatio(NumberUtils.format(hPercent));
                tacticCrossStarSnipe.setPurchaseDayMinUpRatio(NumberUtils.format(lPercent));
                tacticCrossStarSnipe.setPurchaseDayQuantity(kLineData.getVolume());
                tacticCrossStarSnipe.setPreviousDayOpenPrice(BigDecimal.valueOf(previousKLine.getOpen()));
                tacticCrossStarSnipe.setPreviousDayClosePrice(BigDecimal.valueOf(previousKLine.getClose()));
                tacticCrossStarSnipe.setPreviousDayMaxPrice(BigDecimal.valueOf(previousKLine.getHigh()));
                tacticCrossStarSnipe.setPreviousDayMinPrice(BigDecimal.valueOf(previousKLine.getLow()));
                tacticCrossStarSnipe.setPreviousDayCloseUpRatio(previousKLine.getUpPercentage());

                tacticCrossStarSnipe.setPreviousDayQuantity(previousKLine.getVolume());

                tacticCrossStarSnipe.setFirstAdjustQuantity(firstAdjust.getVolume());
                tacticCrossStarSnipe.setAdjustDays(endIndex - limitIndex);

                if (limitUp.getContinuousCount() == 1) {
                    if (c >= limitKLineData.getClose()) {
                        tacticCrossStarSnipe.setGuardPrice(BigDecimal.valueOf(limitKLineData.getClose()));
                    } else {
                        tacticCrossStarSnipe.setGuardPrice(BigDecimal.valueOf(limitKLineData.getLow()));
                    }
                }
                if (limitUp.getContinuousCount() == 2) {
                    if (c > limitKLineData.getClose()) {
                        tacticCrossStarSnipe.setGuardPrice(BigDecimal.valueOf(limitKLineData.getClose()));
                    } else {
                        HisDailyLine firstLimitData = hisDailyLineAccessor.getPreviousDayLine(code, DateUtil.formatDate(limitKLineData.getDate()));
                        double min = Math.min(firstLimitData.getClose(), limitKLineData.getLow());
                        tacticCrossStarSnipe.setGuardPrice(BigDecimal.valueOf(min));
                    }
                }

                if (!date.equals(TradingDayCache.getLastTradingDay())) {
                    int buyIndex = allKLineData.stream().map(e -> DateUtil.formatDate(e.getDate())).collect(Collectors.toList()).indexOf(date);
                    double maxPrice = allKLineData.get(buyIndex).getClose();
                    double minPrice = allKLineData.get(buyIndex).getClose();
                    for (int i = buyIndex + 1; i < buyIndex + 6; i++) {
                        if (i == allKLineData.size()) break;
                        maxPrice = Math.max(allKLineData.get(i).getHigh(), maxPrice);
                        minPrice = Math.min(allKLineData.get(i).getClose(), minPrice);
                        if (minPrice < tacticCrossStarSnipe.getGuardPrice().doubleValue()) break;
                    }
                    double maxProfit = NumberUtils.format((maxPrice - c) / c * 100);
                    double maxLoss = NumberUtils.format((minPrice - c) / c * 100);

                    tacticCrossStarSnipe.setMaxProfitPrice(BigDecimal.valueOf(maxPrice));
                    tacticCrossStarSnipe.setMaxProfitRatio(maxProfit);
                    tacticCrossStarSnipe.setMaxLossPrice(BigDecimal.valueOf(minPrice));
                    tacticCrossStarSnipe.setMaxLossRatio(maxLoss);
                }
                System.out.println(company.getCode() + ":　" + company.getName());
                tacticCrossStarSnipeAccessor.save(tacticCrossStarSnipe);
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
        return true;
    }

    @Override
    public boolean refreshProfit(Integer limit) {
        List<String> recentTradingDays = TradingDayCache.getRecentTradingDays(limit + 1);
        recentTradingDays.remove(TradingDayCache.getLastTradingDay());

        List<TacticCrossStarSnipe> list = tacticCrossStarSnipeAccessor.list(
                new LambdaQueryWrapper<TacticCrossStarSnipeDO>().in(TacticCrossStarSnipeDO::getDate, recentTradingDays));
        list.forEach(tacticCrossStarSnipe -> {
            String date = DateUtil.formatDate(tacticCrossStarSnipe.getDate());
            Company company = companyAccessor.getById(tacticCrossStarSnipe.getCompanyId());
            List<FetchDailyLineResult> allKLineData = remoteApiFacade.fetchAllDailyLines(company.getCode());
            int buyIndex = allKLineData.stream().map(FetchDailyLineResult::getDate).collect(Collectors.toList()).indexOf(date);
            Double c = allKLineData.get(buyIndex).getPrice();
            double maxPrice = allKLineData.get(buyIndex).getPrice();
            double minPrice = allKLineData.get(buyIndex).getPrice();
            for (int i = buyIndex + 1; i < buyIndex + limit + 1; i++) {
                if (i == allKLineData.size()) break;
                maxPrice = Math.max(allKLineData.get(i).getHigh(), maxPrice);
                minPrice = Math.min(allKLineData.get(i).getPrice(), minPrice);
                if (minPrice < tacticCrossStarSnipe.getGuardPrice().doubleValue()) break;
            }
            double maxProfit = NumberUtils.format((maxPrice - c) / c * 100);
            double maxLoss = NumberUtils.format((minPrice - c) / c * 100);

            if (limit == 1) {
                tacticCrossStarSnipe.setMaxProfitPriceNextDay(BigDecimal.valueOf(maxPrice));
                tacticCrossStarSnipe.setMaxProfitRatioNextDay(maxProfit);
                tacticCrossStarSnipe.setMaxLossPriceNextDay(BigDecimal.valueOf(minPrice));
                tacticCrossStarSnipe.setMaxLossRatioNextDay(maxLoss);
            }
            if (limit == 5) {
                tacticCrossStarSnipe.setMaxProfitPrice(BigDecimal.valueOf(maxPrice));
                tacticCrossStarSnipe.setMaxProfitRatio(maxProfit);
                tacticCrossStarSnipe.setMaxLossPrice(BigDecimal.valueOf(minPrice));
                tacticCrossStarSnipe.setMaxLossRatio(maxLoss);
            }
            if (limit == 10) {
                tacticCrossStarSnipe.setMaxProfitPriceTenDays(BigDecimal.valueOf(maxPrice));
                tacticCrossStarSnipe.setMaxProfitRatioTenDays(maxProfit);
                tacticCrossStarSnipe.setMaxLossPriceTenDays(BigDecimal.valueOf(minPrice));
                tacticCrossStarSnipe.setMaxLossRatioTenDays(maxLoss);
            }

            tacticCrossStarSnipeAccessor.updateById(tacticCrossStarSnipe);
        });
        return true;
    }

}
